<?php

/**
 * SigmaCMS - Content Management System
 *
 * Copyright (C) 2008 Pavol Biely
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package    SigmaCMS
 * @author     Pavol Biely <pavolbiely@gmail.com>
 * @copyright  2008 Pavol Biely
 * @license    http://www.gnu.org/licenses/gpl.txt   GNU General Public License
 * @link       http://pabi3.com/
 * @since      0.1.0
 */

final class Admin_Plugins
{
	/**
	 * Initialization
	 *
	 * @return void
	 */
	public function initialize()
	{
		Admin::header(_ADMIN_PLUGINS);

		echo "<h1>"._ADMIN_PLUGINS."</h1>\n";

		$sql = db::query("SELECT `id`,`name`,`author`,`version`,`active` FROM ".db::prefix('plugins')." ORDER BY `priority` DESC");
		if (db::numRows($sql) > 0) {
			echo "<table>\n";
			echo "<colgroup>\n";
			echo "<col width='30%' align='left' />\n";
			echo "<col width='25%' align='left' />\n";
			echo "<col width='10%' align='center' />\n";
			echo "<col width='10%' align='center' />\n";
			echo "<col width='25%' align='center' />\n";
			echo "</colgroup>\n";
			echo "<tr>\n";
			echo "<th>"._ADMIN_PLUGINS_NAME."</th>\n";
			echo "<th>"._ADMIN_PLUGINS_AUTHOR."</th>\n";
			echo "<th>"._ADMIN_PLUGINS_VERSION."</th>\n";
			echo "<th>"._ADMIN_PLUGINS_STATUS."</th>\n";
			echo "<th>"._ADMIN_ACTIONS."</th>\n";
			echo "</tr>\n";
			while ($row = db::fetchObject($sql)) {
				echo "<tr>\n";
				echo "<td>\n";
				if (self::hasAdmin($row->id)) {
					echo "<a href='index.php?action=plugin.{$row->id}' title='"._ADMIN." - ".$row->name."'>".$row->name."</a><br />\n";
				} else {
					echo $row->name."<br />\n";
				}
				echo "<small>{$row->id}</small>\n";
				echo "</td>\n";
				echo "<td>".$row->author."</td>\n";
				echo "<td>".$row->version."</td>\n";
				echo "<td>".($row->active ? Admin::image('plugin',_ADMIN_PLUGINS_STATUS_ENABLED) : Admin::image('plugin_disabled',_ADMIN_PLUGINS_STATUS_DISABLED))."</td>\n";
				echo "<td>\n";
				echo "<a href='index.php?action=plugins&amp;page=help&amp;id=".$row->id."' title='"._ADMIN_PLUGINS_HELP."'>".Admin::image('help',_ADMIN_PLUGINS_HELP)."</a>\n";
				echo "<a href='index.php?action=plugins&amp;page=priority&amp;shift=increase&amp;id=".$row->id."' title='"._ADMIN_PLUGINS_PRIORITY_UP."'>".Admin::image('arrow_up',_ADMIN_PLUGINS_PRIORITY_UP)."</a>\n";
				echo "<a href='index.php?action=plugins&amp;page=priority&amp;shift=decrease&amp;id=".$row->id."' title='"._ADMIN_PLUGINS_PRIORITY_DOWN."'>".Admin::image('arrow_down',_ADMIN_PLUGINS_PRIORITY_DOWN)."</a>\n";
				echo "<a href='index.php?action=plugins&amp;page=edit&amp;id=".$row->id."' title='"._ADMIN_PLUGINS_EDIT."'>".Admin::image('plugin_edit',_ADMIN_PLUGINS_EDIT)."</a>\n";
				echo "<a href='index.php?action=plugins&amp;page=uninstall&amp;id=".$row->id."&amp;sure=0' title='"._ADMIN_PLUGINS_UNINSTALL."'>".Admin::image('plugin_uninstall',_ADMIN_PLUGINS_UNINSTALL)."</a>\n";
				echo "</td>\n";
				echo "</tr>\n";
			}
			echo "</table>\n";
		} else {
			echo "<p>"._ADMIN_PLUGINS_NO."</p>\n";
		}
		db::free($sql);

		Admin::footer();
	}

	/**
	 * Menu
	 *
	 * @return void
	 */
	public function menu()
	{
		echo "<h2>"._ADMIN_PLUGINS."</h2>\n";
		echo "<ul>\n";
		echo "<li><a href='index.php?action=plugins&amp;page=install' title='"._ADMIN_PLUGINS_ADD."'>"._ADMIN_PLUGINS_ADD."</a></li>\n";
		echo "</ul>\n";
	}

	///////////////////////////////////////////////////////////////////////////

	/**
	 * Change plugin settings
	 * 
	 * @return void
	 */
	public function page_edit()
	{
		// ID must be set
		if (!isset($_GET['id']) || !Plugin::isInstalled(_get('id'))) {
			Admin::redirect('index.php?action=plugins');
		}

		// ID
		$pluginId = _get('id');

		Admin::header(_ADMIN_PLUGINS,_ADMIN_PLUGINS_EDIT);

		echo "<h1>"._ADMIN_PLUGINS_EDIT."</h1>\n";

		$sql = db::query("SELECT `id`,`priority`,`active` FROM ".db::prefix('plugins')." WHERE `id`='".db::escape($pluginId)."'");
		$row = db::fetchObject($sql);
		if (db::numRows($sql) == 0) {
			echo "<p class='red'>"._ADMIN_PLUGINS_NOT_FOUND."</p>\n";
			Admin::footer();
		}

		if (isset($_POST['submit'])) {
			// priority
			$priority = abs(intval(_post('priority')));
			if ($priority <= 0 && $priority >= 100) {
				$priority = $row->priority;
			}

			// active?
			$active = _post('active') ? '1' : '0';

			// update plugin settings
			db::query("UPDATE ".db::prefix('plugins')." SET `priority`='".db::escape($priority)."',`active`='".db::escape($active)."' WHERE `id`='".db::escape($pluginId)."'");

			// event after plugin edit
			Admin::addEvent('pluginEdit',$pluginId);

			Admin::redirect('index.php?action=plugins');
		}

		echo "<form action='index.php?action=plugins&amp;page=edit&amp;id=".$row->id."' method='post'>\n";
		echo "<fieldset>\n";

		echo "<label for='priority'>"._ADMIN_PLUGINS_PRIORITY." <small>(1-99)</small></label>\n";
		echo "<input id='priority' name='priority' type='text' value='".$row->priority."' maxlength='2' />\n";

		echo "<label for='active'>"._ADMIN_PLUGINS_ACTIVE."</label>\n";
		echo "<input id='active' name='active' type='checkbox' ".($row->active ? "checked='checked'" : NULL)." value='1' />\n";

		echo "<p class='center'>\n";
		echo "<input name='submit' type='submit' value='"._ADMIN_EDIT."' />\n";
		echo "</p>\n";

		echo "</fieldset>\n";
		echo "</form>\n";

		Admin::footer();
	}

	/**
	 * Change plugin priority
	 *
	 * @return void
	 */
	public function page_priority()
	{
		// ID muset be set
		if (!isset($_GET['id']) || !Plugin::isInstalled(_get('id'))) {
			Admin::redirect('index.php?action=plugins');
		}

		// ID
		$pluginId = _get('id');

		// current plugin priority
		$priority  = intval(db::fQuery("SELECT `priority` FROM ".db::prefix('plugins')." WHERE `id`='".db::escape($pluginId)."'"));

		// increase or decrease priority
		if (_get('shift') == 'increase') {
			$newPriority = intval(db::fQuery("SELECT `priority` FROM ".db::prefix('plugins')." WHERE `priority`>$priority LIMIT 1"));
			if ($newPriority == 0) {
				$newPriority = $priority;
			}
			$newPriority += 1;
			if ($newPriority > 99) {
				$newPriority = 99;
			}

			// update priority
			db::query("UPDATE ".db::prefix('plugins')." SET `priority`='$newPriority' WHERE `id`='".db::escape($pluginId)."' AND `priority`<100 LIMIT 1");

			// event after increasing plugin priority
			Admin::addEvent('pluginPriorityUp',$pluginId);
		} else if (_get('shift') == 'decrease') {
			$newPriority = intval(db::fQuery("SELECT `priority` FROM ".db::prefix('plugins')." WHERE `priority`<$priority LIMIT 1"));
			if ($newPriority == 0) {
				$newPriority = $priority;
			}
			$newPriority -= 1;
			if ($newPriority < 1) {
				$newPriority = 1;
			}

			// update priority
			db::query("UPDATE ".db::prefix('plugins')." SET `priority`='$newPriority' WHERE `id`='".db::escape($pluginId)."' AND `priority`>0 LIMIT 1");

			// event after decreasing plugin priority
			Admin::addEvent('pluginPriorityDown',$pluginId);
		}

		Admin::redirect('index.php?action=plugins');
	}

	/**
	 * Plugin installation
	 *
	 * @return void
	 */
	public function page_install()
	{
		Admin::header(_ADMIN_PLUGINS_INSTALL);

		echo "<h1>"._ADMIN_PLUGINS_INSTALL."</h1>\n";

		if (isset($_POST['install'])) {
			// name and path to plugin
			$plugin = Media::clearPath(_post('plugin'));
			$path   = dirname(__FILE__).'/../../'.cfg::DIR_PLUGINS.cfg::PLUGIN_PREFIX.$plugin.'.php';

			// does the plugin exist?
			if (file_exists($path)) {
				// include plugin file
				include_once $path;

				// installation result
				$install = true;

				// is there any plugin with the same ID installed?
				if (Plugin::isInstalled($plugin)) {
					// installation cannot continue
					$install = false;

					echo "<p class='red'>".sprintf(_ADMIN_PLUGINS_INSTALL_ALREADY_EXISTS,$plugin)."</p>\n";
				}

				// plugin class name
				$class = 'Plugin_'.ucfirst(strtolower($plugin));

				// create plugin object
				$plugin = new $class;

				// dependencies
				$dependency = $plugin->dependency();

				// plugin must be an instance of PluginAPI class/interface
				if (!$plugin instanceof PluginAPI) {
					$install = false;
					echo "<p class='red'>"._ADMIN_PLUGINS_INSTALL_NOT_INSTANCEOF_PLUGINAPI."</p>\n";
				}

				// plugin requires other plugins for running?
				else if (is_array($dependency)) {
					$needed = array();

					for ($i = 0; $i < count($dependency); $i++) {
						if (!Plugin::isInstalled($dependency[$i])) {
							$needed[] = $dependency[$i];
						}
					}

					if (count($needed) > 0) {
						$install = false;

						echo "<p class='red'>"._ADMIN_PLUGINS_INSTALL_FOLLOWING_NEEDED."</p>\n";
						echo "<ul>\n";
						for ($i = 0; $i < count($dependency); $i++) {
							echo "<li>";
							echo $dependency[$i];
							if ($needed[$i] == $dependency[$i]) {
								echo _ADMIN_PLUGINS_INSTALL_NOT_INSTALLED;
							} else {
								echo _ADMIN_PLUGINS_INSTALL_INSTALLED;
							}
							echo "</li>\n";
						}
						echo "</ul>\n";
					}
				}

				// installation cannot continue
				if (!$install) {
					Admin::footer();
					return;
				}

				// begin installation process
				$install = $plugin->install();

				// is it possible to install plugin?
				if ($install) {
					// get plugin information
					$name    = $plugin->name();
					$author  = $plugin->author();
					$version = $plugin->version();

					// priority
					$priority = abs(intval(_post('priority')));
					$priority = ($priority <= 0 && $priority >= 100) ? 50 : $priority;

					// prepare query
					$sql = "INSERT INTO ".db::prefix('plugins')."(`id`,`name`,`author`,`version`,`active`,`priority`)"
					     . "VALUES("
					     . "'".db::escape(_post('plugin'))."',"
					     . "'".db::escape($name)."',"
					     . "'".db::escape($author)."',"
					     . "'".db::escape($version)."',"
					     . "'0',"
					     . "".intval($priority)
					     . ")";

					// success?
					if (db::query($sql)) {
						Actionlog::add(sprintf(_LOG_ADMIN_PLUGIN_INSTALL,$name));

						$pluginId = _post('plugin');

						Admin::addEvent('pluginInstall',$pluginId);
						Admin::redirect('index.php?action=plugins');
					} else {
						echo "<p class='red'>"._ADMIN_PLUGINS_INSTALL_SQL_ERROR."</p>\n";
					}
				} else {
					echo "<p class='red'>"._ADMIN_PLUGINS_INSTALL_UNSUCCESSFULL."</p>";
				}
			} else {
				echo "<p class='red'>".sprintf(_ADMIN_PLUGINS_INSTALL_NOT_FOUND,"<em>".$path."</em>")."</p>";
			}

			Admin::footer();
			return;
		}

		echo "<form action='"._hsc(_server('PHP_SELF'))."?action=plugins&amp;page=install' method='post'>\n";
		echo "<fieldset>\n";

		// plugins/ directory
		$path = dirname(__FILE__).'/../../'.cfg::DIR_PLUGINS;
		$dir  = scandir($path);

		// skip . and ..
		array_splice($dir,0,2);

		$install = array();

		for ($i = 0; $i < count($dir); $i++) {
			if (is_file($path.$dir[$i])) {
				// plugin filename
				$pluginName = substr($dir[$i],0,-4); // remove ".php"
				$pluginName = substr($pluginName,strlen(cfg::PLUGIN_PREFIX)); // remove prefix "plugin." (cfg::PLUGIN_PREFIX)

				if (!Plugin::isInstalled($pluginName)) {
					$install[] = $pluginName;
				}
			}
		}

		// available plugins
		$available = count($install);

		// are there any available plugins?
		if ($available > 0) {
			echo "<label for='plugin'>"._ADMIN_PLUGINS."</label>\n";
			echo "<select id='plugin' name='plugin'>\n";
			for ($i = 0; $i < $available; $i++) {
				echo "<option value='{$install[$i]}'>{$install[$i]}</option>\n";
			}
			echo "</select>\n";

			echo "<label for='priority'>"._ADMIN_PLUGINS_PRIORITY."</label>\n";
			echo "<input id='priority' name='priority' type='text' value='".(_post('priority') ? abs(intval(_post('priority'))) : 50)."' maxlength='2' class='small' />\n";

			echo "<p class='center'>\n";
			echo "<input name='install' type='submit' value='"._ADMIN_PLUGINS_INSTALL_SUBMIT."' />\n";
			echo "</p>\n";
		} else {
			echo "<p>"._ADMIN_PLUGINS_NO_TO_INSTALL."</p>\n";
		}

		echo "</fieldset>\n";
		echo "</form>\n";

		Admin::footer();
	}

	/**
	 * Uninstall plugin
	 *
	 * @return void
	 */
	public function page_uninstall()
	{
		// ID must be set
		if (!isset($_GET['id']) || !Plugin::isInstalled(_get('id'))) {
			Admin::redirect('index.php?action=plugins');
		}

		Admin::header(_ADMIN_PLUGINS_UNINSTALL);

		// ID
		$pluginId = _get('id');

		if (_get('sure') == 1) {
			// if the plugin is not registered (it occurrs if plugin is not activated)
			if (!Plugin::isRegistered($pluginId)) {
				// register plugin
				Plugin::register($pluginId);
			}

			// load instance and name of plugin
			$pluginObj = Plugin::getPlugin($pluginId);

			// if plugin does not have an object, it is supposed it is no longer present on the server
			if (!is_object($pluginObj)) {
				echo "<h2>"._ADMIN_ERROR."</h2>\n";
				echo "<p class='red'>"._ADMIN_PLUGINS_UNINSTALL_ERROR."</p>\n";
				Admin::footer();
				return;
			}

			// get plugin name
			$pluginName = $pluginObj->name();

			// uninstall plugin and load his output from buffer
			ob_start();
			$uninstall = $pluginObj->uninstall();
			$uninstallBuffer = ob_get_contents();
			ob_end_clean();

			// success?
			if ($uninstall) {
				// delete plugin from database
				db::query("DELETE FROM ".db::prefix('plugins')." WHERE `id`='".db::escape($pluginId)."'");

				Actionlog::add(sprintf(_LOG_ADMIN_PLUGIN_UNINSTALL,$pluginName));

				// odregistrovať plugin
				Plugin::unregister(get_class($pluginObj));

				Admin::addEvent('pluginUninstall',$pluginId);
				Admin::redirect('index.php?action=plugins');
			} else {
				echo "<h2>"._ADMIN_ERROR."</h2>\n";
				echo "<p class='red'>"._ADMIN_PLUGINS_UNINSTALL_UNSUCCESSFULL."</p>\n";

				// print output of uninstall method
				echo $uninstallBuffer;
			}
		} else {
			Admin::confirm("index.php?action=plugins&page=uninstall&id=$pluginId&sure=1");
		}

		Admin::footer();
	}

	/**
	 * Help - display available actions, events and keywords
	 *
	 * @return void
	 */
	public function page_help()
	{
		// ID must be set
		if (isset($_GET['id'])) {
			$pluginId = _get('id');
		} else {
			Admin::redirect('index.php?action=plugins');
		}

		Admin::header(_ADMIN_PLUGINS,_ADMIN_PLUGINS_HELP,$pluginId);

		// get plugin name
		$pluginName = db::fQuery("SELECT `name` FROM ".db::prefix('plugins')." WHERE `id`='".db::escape($pluginId)."'");
		if (!$pluginName) {
			$pluginName = $pluginId;
		}

		echo "<h1>"._ADMIN_PLUGINS_HELP." &ndash; "._hsc($pluginName)."</h1>\n";

		// plugin is not installed?
		if (!Plugin::isInstalled($pluginId)) {
			echo "<p class='red'>".sprintf(_ADMIN_PLUGINS_NOT_INSTALLED,$pluginName)."</p>\n";
			Admin::footer();
			exit;
		}

		// plugin is not registered?
		if (!Plugin::isRegistered($pluginId)) {
			Plugin::register($pluginId);
		}

		// methods of plugin class
		$keywords = (array) $this->getPublicMethods(Plugin::getPlugin($pluginId));

		// special methods
		$parse = $event = $action = array();

		// get method name
		$match = array();
		foreach ($keywords as $key => $method) {
			preg_match('/^(parse|event|action)_(.+)/i',$method,$match);
			switch($match[1]) {
				case 'parse':
					$parse[] = $match[2];
					break;
				case 'event':
					$event[] = $match[2];
					break;
				case 'action':
					$action[] = $match[2];
					break;
			}
		}

		$special = array('parse','event','action');
		foreach ($special as $key => $specialMethod) {
			if (count($$specialMethod) > 0) {
				$constName = "_ADMIN_PLUGINS_HELP_".strtoupper($specialMethod);
				echo "<h3>".constant($constName)." <small>(".$specialMethod."_)</small></h3>\n";
				echo "<ul>\n";
				foreach ($$specialMethod as $key2 => $value) {
					$methodName = $specialMethod.'_'.$value;
					$args = new ReflectionMethod(Plugin::getPlugin($pluginId),$methodName);
					$args = $args->getParameters();
					echo "<li>";
					if ($specialMethod == 'parse') echo "&lt;%$pluginId(<span class='blue'>".$value."</span>";
					else if ($specialMethod == 'event') echo "&lt;%event(<span class='blue'>".$value."</span>";
					else if ($specialMethod == 'action') {
						$url = cfg::URL."index.php?plugin=<span class='blue'>$pluginId</span>&amp;method=<span class='blue'>".$value."</span>";
						echo "<a href='".strip_tags($url)."'>$url</a>\n";
					}
					if ($specialMethod == 'parse') {
						$count = count($args);
						if ($count > 0) {
							for ($i = 0; $i < $count; $i++) {
								echo ",".$args[$i]->name;
							}
						}
					}
					if ($specialMethod == 'parse' || $specialMethod == 'event') echo ")%&gt;";
					echo "</li>\n";
				}
				echo "</ul>\n";
			}
		}

		// plugin does not contains any special methods
		if (count($parse) == 0 && count($event) == 0 && count($action) == 0) {
			echo "<p class='red'>"._ADMIN_PLUGINS_HELP_NOSPECIAL."</p>\n";
		}

		Admin::footer();
	}

	/**
	 * Check if the plugin has administration
	 *
	 * @param integer $pluginId
	 * @return boolean
	 */
	private function hasAdmin($pluginId)
	{
		// path to plugin administration file
		$path = dirname(__FILE__).'/../../'.cfg::DIR_PLUGINS.cfg::PLUGIN_PREFIX.$pluginId.'/admin.php';

		// does the file exist?
		if (file_exists($path)) {
			// include file
			include_once $path;

			// admin class name
			$className = 'Admin_Plugin_'.ucfirst(strtolower($pluginId));

			if (!class_exists($className)) {
				return false;
			}

			if (in_array('initialize',get_class_methods($className))) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Load public methods of class to array
	 *
	 * @param mixed $className
	 * @return array
	 */
	private function getPublicMethods($className) {
	    $result = array();
	    foreach (get_class_methods($className) as $method) {
	        $reflect = new ReflectionMethod($className, $method);
	        if($reflect->isPublic()) {
	            array_push($result,$method);
	        }
	    }
	    return $result;
	}
}
